package com.jack006.zuul.filters;

import com.jack006.grace.result.GraceJSONResult;
import com.jack006.grace.result.ResponseStatusEnum;
import com.jack006.utils.IpUtils;
import com.jack006.utils.JsonUtils;
import com.jack006.utils.RedisOperator;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @Classname ZuulFilter
 * @Description构建黑名单的自定义过滤器
 * @Date 2021-08-07 22:08
 * @Created by jack
 */
@Component
@RefreshScope
public class BlackIPFilter extends ZuulFilter {

    @Value("${blackIp.continueCounts}")
    public Integer continueCounts;
    @Value("${blackIp.timeInterval}")
    public Integer timeInterval;
    @Value("${blackIp.limitTimes}")
    public Integer limitTimes;

    @Autowired
    private RedisOperator redisOperator;

    /**
     * 定义过滤器的类型
     *   pre: 请求之前
     *   route: 在路上
     *   post: 请求之后
     *   error： 处理请求时发生错误的时候执行
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 过滤器执行的顺序：配置多个有顺序的过滤
     * 执行顺序从小到大
     * @return
     */
    @Override
    public int filterOrder() {
        return 2;
    }

    /**
     * 是否开启过滤器
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤器的业务实现
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {

        System.out.println("执行【ip黑名单】过滤器...");

        System.out.println("continueCounts: " + continueCounts);
        System.out.println("timeInterval: " + timeInterval);
        System.out.println("limitTimes: " + limitTimes);

        // 获得上下文对象
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();

        // 获得 ip
        String requestIp = IpUtils.getRequestIp(request);

        /**
         * 判断 ip 在10秒内的请求次数是否超过10次
         * 如果超过,则限制这个 ip 访问15秒，15秒以后再放行
         */

        final String ipRedisKey = "zuul-ip:"+requestIp;
        final String ipRedisLimitKey = "zuul-ip-limit:"+requestIp;

        // 获得当前 ip这个 key的剩余时间
        long limitLeftTime = redisOperator.ttl(ipRedisLimitKey);
        // 如果当前限制 ip 的 key还存在剩余时间
        if (limitLeftTime > 0) {
            stopRequest(context);
            return null;
        }

        // 在 redis 中累加 ip 的请求访问次数
        long requestCounts = redisOperator.increment(ipRedisKey,1);
        // 从0开始计算请求次数，初期访问为1，则设置过期时间，也就是连续请求的间隔时间
        if (requestCounts == 1) {
            redisOperator.expire(ipRedisKey,timeInterval);
        }

        // 如果还能获得请求时间，说明用户连续请求的次数落在10秒内
        // 一旦请求次数超过了连续访问的次数，则需要限制这个 ip的访问
        if (requestCounts > continueCounts) {
            // 限制 ip的访问时间
            redisOperator.set(ipRedisLimitKey,ipRedisLimitKey,limitLeftTime);
            stopRequest(context);
        }

        return null;
    }

    private void stopRequest(RequestContext context) {
        // 停止 zuul 继续向下路由，禁止请求通信
        context.setSendZuulResponse(false);
        context.setResponseStatusCode(200);
        String result = JsonUtils.objectToJson(
                GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR_ZUUL));
        context.setResponseBody(result);
        context.getResponse().setCharacterEncoding("UTF-8");
        context.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
    }
}
